package com.iotimc.networklibrary.core;

import android.content.Context;
import android.os.Environment;

import com.alibaba.fastjson.JSONObject;
import com.iotimc.networklibrary.entity.BaseEntity;
import com.iotimc.networklibrary.entity.DownInfo;
import com.iotimc.networklibrary.entity.Result;
import com.iotimc.networklibrary.entity.Result2;
import com.iotimc.networklibrary.service.DownListener;
import com.iotimc.networklibrary.service.DownloadService;
import com.iotimc.networklibrary.service.FileService;
import com.iotimc.networklibrary.service.SimpleService;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Predicate;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import okio.BufferedSink;
import okio.GzipSink;
import okio.Okio;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

/**
 * Created by david on 2017/6/23.
 *
 * @author david
 */

public class HttpRetrofitFactory {
    private final static String TAG = "HttpRetrofitFactory";
    private static String baseUrl = "http://192.168.8.16:8280/terminal/";
    /**
     * 缓存文件最大限制大小20M
     */
    private static final long CACHE_SIZE = 1024 * 1024 * 20;

    private static String cacheDirectory = Environment.getExternalStorageDirectory() + "/okttpcaches";

    private static Cache cache = new Cache(new File(cacheDirectory), CACHE_SIZE);

    private OkHttpClient okHttpClient;
    private Retrofit retrofit;

    Context context;

    /**
     * 在访问HttpMethods时创建单例
     */
    private static class SingletonHolder {
        private static final HttpRetrofitFactory INSTANCE = new HttpRetrofitFactory();
    }

    public static HttpRetrofitFactory getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private HttpRetrofitFactory() {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        Interceptor interceptor = new Interceptor() {
            @Override
            public Response intercept(Interceptor.Chain chain) throws IOException {
                Request request = chain.request();
                if (!NetUtils.isNetworkAvailable(context)) {
                    request = request.newBuilder()
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                }

                Response response = chain.proceed(request);
                if (NetUtils.isNetworkAvailable(context)) {
                    // 有网络时 设置缓存超时时间0个小时
                    int maxAge = 0 * 60;
                    response.newBuilder()
                            .header("Cache-Control", "public, max-age=" + maxAge)
                            // 清除头信息，因为服务器如果不支持，会返回一些干扰信息，不清除下面无法生效
                            .removeHeader("Pragma")
                            .build();
                } else {
                    // 无网络时，设置超时为4周
                    int maxStale = 60 * 60 * 24 * 28;
                    response.newBuilder()
                            .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                            .removeHeader("Pragma")
                            .build();
                }
                return response;
            }
        };
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(logging)
                .addInterceptor(new GzipRequestInterceptor())
                //.addInterceptor(interceptor)
                //   .addInterceptor(basicParamsInterceptor)
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                // 设置缓存
                .cache(cache)
                //错误重连
                .retryOnConnectionFailure(true)
                .build();

        retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(FastJsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .build();
    }

    static class GzipRequestInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request originalRequest = chain.request();
            if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
                return chain.proceed(originalRequest);
            }

            Request compressedRequest = originalRequest.newBuilder().header("Content-Encoding", "gzip")
                    .method(originalRequest.method(), gzip(originalRequest.body())).build();
            return chain.proceed(compressedRequest);
        }

        private RequestBody gzip(final RequestBody body) {
            return new RequestBody() {

                @Override
                public MediaType contentType() {
                    return body.contentType();
                }

                @Override
                public long contentLength() {
                    return -1; // 无法知道压缩后的数据大小
                }

                @Override
                public void writeTo(BufferedSink sink) throws IOException {
                    BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                    body.writeTo(gzipSink);
                    gzipSink.close();
                }
            };
        }
    }


    public <T> T create(Class<T> service) {
        return retrofit.create(service);
    }

    public <T> T create(Class<T> service, OkHttpClient okHttpClient) {
        return retrofit.newBuilder()
                .client(okHttpClient)
                .build().create(service);
    }


    public void upload(String url, ProgressListener progressListener, BaseObserver<Integer> baseObserver, File... files) {
        FileService fileService = create(FileService.class);
        UploadFileRequestBody uploadFileRequestBody =
                new UploadFileRequestBody(files[0], progressListener);

        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addPart(Headers.of("Content-Disposition",
                        "form-data; name=\"" + files[0].getName() + "\"; filename=\"" + files[0].getName() + "\""),
                        uploadFileRequestBody).build();

        fileService.upload(url, multipartBody)
                .compose(RxSchedulers.<BaseEntity<Integer>>compose())
                .subscribe(baseObserver);
    }


    /**
     * 下载文件方法
     */
    public Observable<DownInfo> downloadFile(DownInfo downInfo, ProgressListener listener) {
        //增加拦截器
        OkHttpClient client = okHttpClient.newBuilder().addNetworkInterceptor(new DownloadInterceptor(listener)).build();
        DownloadService fileService = create(DownloadService.class, client);
        return fileService.downloadFile(downInfo.getLocalPath()).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()).unsubscribeOn(Schedulers.io())
                .map(new DownLoadFunction(downInfo));
    }

    /**
     * 下载文件方法
     */
    public void downloadFile(DownInfo downInfo, ProgressListener listener, final DownListener downListener) {
        //增加拦截器
        OkHttpClient client = okHttpClient.newBuilder().addNetworkInterceptor(new DownloadInterceptor(listener)).build();
        DownloadService fileService = create(DownloadService.class, client);
        fileService.downloadFile(downInfo.getLocalPath()).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()).unsubscribeOn(Schedulers.io())
                .map(new DownLoadFunction(downInfo)).filter(new Predicate<DownInfo>() {
            @Override
            public boolean test(DownInfo downInfo) throws Exception {
                boolean md5Result = downInfo.getLocalMd5().equals(downInfo.getServerMd5());
                if (!md5Result) {
                    downListener.md5Result(md5Result);
                }
                return md5Result;
            }
        })
                .subscribe(new Observer<DownInfo>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(DownInfo value) {
                        downListener.onDownSuccess(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        downListener.onDownError(e);
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }


    /**
     * 需要返回结果 Result 里（row是list<entity>的）
     *
     * @param url
     * @param json
     * @param baseObserver
     */
    public void getResultToListEntity(String url, String json, BaseObserver baseObserver) {
        create(SimpleService.class)
                .get2(url, json)
                .compose(RxSchedulers.<BaseEntity<Result2>>compose())
                .subscribe(baseObserver);
    }

    /**
     * 需要返回结果 Result 里（row是JSONArray的）
     *
     * @param url
     * @param json
     * @param baseObserver
     */
    public void getResultToJSONArray(String url, String json, BaseObserver baseObserver) {
        create(SimpleService.class)
                .get(url, json)
                .compose(RxSchedulers.<BaseEntity<Result>>compose())
                .subscribe(baseObserver);
    }

    /**
     * 需要返回结果 Result 由传入的T 决定
     *
     * @param url
     * @param map
     */
    public Observable<JSONObject> getResultToObject(String url, Map map) {
        return create(SimpleService.class)
                .get3(url, map)
                .compose(RxSchedulers.<JSONObject>compose());

    }


    /**
     * 需要返回结果 Result 里（row是list<entity>的）
     *
     * @param url
     * @param json
     * @param baseObserver
     */
    public void postResultToListEntity(String url, Map json, BaseObserver baseObserver) {
        create(SimpleService.class)
                .post2(url, json)
                .compose(RxSchedulers.<BaseEntity<Result2>>compose())
                .subscribe(baseObserver);
    }

    /**
     * 需要返回结果 Result 里（row是JSONArray的）
     *
     * @param url
     * @param json
     * @param baseObserver
     */
    public void postResultToJSONArray(String url, Map json, BaseObserver baseObserver) {
        create(SimpleService.class)
                .post(url, json)
                .compose(RxSchedulers.<BaseEntity<Result>>compose())
                .subscribe(baseObserver);
    }

    /**
     * 需要返回结果 Result 由传入的T 决定
     *
     * @param url
     * @param json
     * @param baseObserver
     */
    public void postToObject(String url, Map json, BaseObserver baseObserver) {
        create(SimpleService.class)
                .post3(url, json)
                .compose(RxSchedulers.<BaseEntity>compose())
                .subscribe(baseObserver);
    }


}
